﻿using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using CodeContrib.Web.Routing;

namespace CodeContrib.Web.Mvc
{
	public static class UrlExtensions
	{
		public static string AbsoluteAction(this UrlHelper url, string action)
		{
			return AbsoluteUrl(url, url.Action(action), false);
		}

		public static string AbsoluteAction(this UrlHelper url, string action, object routeValues)
		{
			return AbsoluteUrl(url, url.Action(action, routeValues), false);
		}

		public static string AbsoluteAction(this UrlHelper url, string action, string controller)
		{
			return AbsoluteUrl(url, url.Action(action, controller), false);
		}

		public static string AbsoluteAction(this UrlHelper url, string action, string controller, object routeValues)
		{
			return AbsoluteUrl(url, url.Action(action, controller, routeValues), false);
		}

		public static string AbsoluteRouteUrl(this UrlHelper url, string routeName)
		{
			return AbsoluteUrl(url, url.RouteUrl(routeName), false);
		}

		public static string AbsoluteRouteUrl(this UrlHelper url, string routeName, object routeValues)
		{
			return AbsoluteUrl(url, url.RouteUrl(routeName, routeValues), false);
		}

		private static string AbsoluteUrl(this UrlHelper url, string relativeUrl)
		{
			return AbsoluteUrl(url, relativeUrl, false);
		}

		private static string AbsoluteUrl(this UrlHelper url, string relativeUrl, bool forceSecureRequest)
		{
			var request = url.RequestContext.HttpContext.Request;
			var requestUrl = request.Url;

			return string.Format("{0}://{1}{2}", (request.IsSecureConnection || forceSecureRequest) ? "https" : "http", requestUrl.Authority, relativeUrl);
		}

		public static string Action(this UrlHelper urlHelper)
		{
			return urlHelper.Action(null);
		}

		public static string Action(this UrlHelper urlHelper, string actionName, object routeValues, string protocol, string hostName, string fragment)
		{
			return UrlHelper.GenerateUrl(null /* routeName */, actionName, null /* controllerName */, protocol, hostName, fragment, routeValues.ToRouteValueDictionary(), urlHelper.RouteCollection, urlHelper.RequestContext, true /* includeImplicitMvcValues */);
		}

		public static string Action(this UrlHelper urlHelper, string actionName, RouteValueDictionary routeValues, string protocol, string hostName, string fragment)
		{
			return UrlHelper.GenerateUrl(null /* routeName */, actionName, null /* controllerName */, protocol, hostName, fragment, routeValues, urlHelper.RouteCollection, urlHelper.RequestContext, true /* includeImplicitMvcValues */);
		}

		public static string Action(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, string protocol, string hostName, string fragment)
		{
			return UrlHelper.GenerateUrl(null /* routeName */, actionName, controllerName, protocol, hostName, fragment, routeValues.ToRouteValueDictionary(), urlHelper.RouteCollection, urlHelper.RequestContext, true /* includeImplicitMvcValues */);
		}

		public static string Action(this UrlHelper urlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName, string fragment)
		{
			return UrlHelper.GenerateUrl(null /* routeName */, actionName, controllerName, protocol, hostName, fragment, routeValues, urlHelper.RouteCollection, urlHelper.RequestContext, true /* includeImplicitMvcValues */);
		}

		public static RouteValueDictionary PreserveQueryString(this UrlHelper urlHelper)
		{
			return PreserveQueryString(urlHelper, null, null);
		}

		public static RouteValueDictionary PreserveQueryString(this UrlHelper urlHelper, object routeValues)
		{
			return PreserveQueryString(urlHelper, routeValues.ToRouteValueDictionary(), null);
		}

		public static RouteValueDictionary PreserveQueryString(this UrlHelper urlHelper, RouteValueDictionary routeValues)
		{
			return PreserveQueryString(urlHelper, routeValues, null);
		}

		public static RouteValueDictionary PreserveQueryString(this UrlHelper urlHelper, object routeValues, IEnumerable<string> excludeKeys)
		{
			return PreserveQueryString(urlHelper, routeValues.ToRouteValueDictionary(), null);
		}

		public static RouteValueDictionary PreserveQueryString(this UrlHelper urlHelper, RouteValueDictionary routeValues, IEnumerable<string> excludeKeys)
		{
			if (routeValues == null)
			{
				routeValues = new RouteValueDictionary();
			}

			if (excludeKeys == null)
			{
				excludeKeys = new string[0];
			}

			foreach (var key in urlHelper.RequestContext.HttpContext.Request.QueryString.AllKeys.Where(x => !excludeKeys.Contains(x)))
			{
				if (key == null)
				{
					continue;
				}

				var value = urlHelper.RequestContext.HttpContext.Request.QueryString[key];

				routeValues.Merge(key, value, false);
			}

			return routeValues;
		}

		public static RouteValueDictionary PreserveRouteValues(this UrlHelper urlHelper)
		{
			return PreserveRouteValues(urlHelper, null, null);
		}

		public static RouteValueDictionary PreserveRouteValues(this UrlHelper urlHelper, object routeValues)
		{
			return PreserveRouteValues(urlHelper, routeValues.ToRouteValueDictionary(), null);
		}

		public static RouteValueDictionary PreserveRouteValues(this UrlHelper urlHelper, RouteValueDictionary routeValues)
		{
			return PreserveRouteValues(urlHelper, routeValues, null);
		}

		public static RouteValueDictionary PreserveRouteValues(this UrlHelper urlHelper, object routeValues, IEnumerable<string> excludeKeys)
		{
			return PreserveRouteValues(urlHelper, routeValues.ToRouteValueDictionary(), excludeKeys);
		}

		public static RouteValueDictionary PreserveRouteValues(this UrlHelper urlHelper, RouteValueDictionary routeValues, IEnumerable<string> excludeKeys)
		{
			if (routeValues == null)
			{
				routeValues = new RouteValueDictionary();
			}

			if (excludeKeys == null)
			{
				excludeKeys = new string[0];
			}

			foreach (var routeValue in urlHelper.RequestContext.RouteData.Values.Where(x => !excludeKeys.Contains(x.Key)))
			{
				routeValues.Merge(routeValue.Key, routeValue.Value, false);
			}

			return routeValues;
		}

		public static string RelativeToAbsolute(this UrlHelper url, string relativeUrl)
		{
			return AbsoluteUrl(url, relativeUrl);
		}

		public static string VersionUrl(this UrlHelper urlHelper, string url, int version)
		{
			if (version > 0)
			{
				var queryString = new NameValueCollection();

				var queryStringIndex = url.IndexOf("?");

				if (queryStringIndex > -1
				    && (queryStringIndex + 1) < url.Length)
				{
					queryStringIndex++;

					var queryStringLength = url.Length - queryStringIndex;

					queryString = url.Substring(queryStringIndex, queryStringLength).ToNameValueCollection();

					// Remove the query string from the URL.
					url = url.Substring(0, --queryStringIndex);
				}

				queryString.Add("v", version.ToString());

				// Add the query string to the URL.
				url += "?";
				url += queryString.Join();
			}

			return url;
		}
	}
}